import { find, filter } from 'lodash';
import { GraphQLToolsResolveMethods } from 'src/SchemaComposer';
import { schemaComposer, graphql } from '../..';

interface IAuthor {
  id: number;
  firstName: string;
  lastName: string;
}

interface IPost {
  id: number;
  authorId: number;
  title: string;
  votes: number;
}

describe('github issue #142: Add schema definition in `graphql-tools` way', () => {
  // example data
  const authors = [
    { id: 1, firstName: 'Tom', lastName: 'Coleman' },
    { id: 2, firstName: 'Sashko', lastName: 'Stubailo' },
    { id: 3, firstName: 'Mikhail', lastName: 'Novikov' },
  ] as IAuthor[];

  const posts = [
    { id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2 },
    { id: 2, authorId: 2, title: 'Welcome to Meteor', votes: 3 },
    { id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1 },
    { id: 4, authorId: 3, title: 'Launchpad is Cool', votes: 7 },
  ] as IPost[];

  const typeDefs = `
  type Author {
    id: Int!
    firstName: String
    lastName: String
    """
    the list of Posts by this author
    """
    posts: [Post]
  }

  type Post {
    id: Int!
    title: String
    author: Author
    votes: Int
  }

  # the schema allows the following query:
  type Query {
    posts: [Post]
    author(id: Int!): Author
  }

  # this schema allows the following mutation:
  type Mutation {
    upvotePost (
      postId: Int!
    ): Post
  }
`;

  const resolvers = {
    Query: {
      posts: () => posts,
      author: (_: any, { id }: any) => find(authors, { id }),
    },

    Mutation: {
      upvotePost: (_: any, { postId }: any) => {
        const post = find(posts, { id: postId });
        if (!post) {
          throw new Error(`Couldn't find post with id ${postId}`);
        }
        post.votes += 1;
        return post;
      },
    },

    Author: {
      posts: (author: IAuthor) => filter(posts, { authorId: author.id }),
    },

    Post: {
      author: (post: IPost) => find(authors, { id: post.authorId }),
    },
  } as GraphQLToolsResolveMethods<any>;

  schemaComposer.addTypeDefs(typeDefs);
  schemaComposer.addResolveMethods(resolvers);
  const schema = schemaComposer.buildSchema();

  it('test graphql query', async () => {
    expect(
      await graphql.graphql({
        schema,
        source: `
          query { 
            author(id: 2) {
              id
              firstName
            }
            posts {
              title
              author {
                firstName
              }
            }
          }
        `,
      })
    ).toEqual({
      data: {
        author: { firstName: 'Sashko', id: 2 },
        posts: [
          { author: { firstName: 'Tom' }, title: 'Introduction to GraphQL' },
          { author: { firstName: 'Sashko' }, title: 'Welcome to Meteor' },
          { author: { firstName: 'Sashko' }, title: 'Advanced GraphQL' },
          { author: { firstName: 'Mikhail' }, title: 'Launchpad is Cool' },
        ],
      },
    });
  });

  it('extend Object type', async () => {
    expect(schemaComposer.Query.getFieldNames()).toEqual(['posts', 'author']);
    schemaComposer.addTypeDefs(`
      extend type Query {
        test: String!
      }
    `);
    expect(schemaComposer.Query.getFieldNames()).toEqual(['posts', 'author', 'test']);
  });
});
